
#include "core/lm.h"
#include "core/shape.h"
#include "shape/disk.h"
#include "shape/sphere.h"
#include "shape/trianglemesh.h"
#include "core/material.h"
#include "core/memory.h"
#include "core/api.h"

#include "io.h"
#include "gltexture.h"

//Ortho (if used)//////////////////////////////////////////////////////////////
double left   = 0.0;        //ortho view volume params
double right  = 0.0;
double bottom = 0.0;
double top    = 0.0;
double zNear  = 0.1;
double zFar   = 10.0;
double fovy    = 45.0;
double prev_z = 0;

//Mouse Interface /////////////////////////////////////////////////////////////
int  mouseX      = 0;       //mouse control variables
int  mouseY      = 0;
bool mouseLeft   = false;
bool mouseMiddle = false;
bool mouseRight  = false;

double dragPosX  = 0.0;     
double dragPosY  = 0.0;
double dragPosZ  = 0.0;


//Modelview Matrix////////////////////////////////////////////////////////////
double matrix[16];          //model matrix and supporting linear algebra functions
double matrixI[16];
double vlen(double x,double y,double z);
void   pos(double *px,double *py,double *pz,const int x,const int y,const int *viewport);
void   getMatrix();
void   invertMatrix(const GLdouble *m, GLdouble *out );

int full_screen = 0;



//Global Variables for this app
Shape* pmodel = NULL;               //the loaded model
vector<Reference<Shape>> shapes;	//refine model
char filename[256];					//model file name

char texfilename[256];				//texture file name
GLTexture *gltexture = NULL;		//gltexture
GLuint mode = 0;                    //viewing mode


int   ww, wh;                       //window width and height
int   wireframe = 0;                //Draw modes
int   show_axis = 0;
int   smooth = 1;      
int   material = 0;
bool   textured = false;
int   two_sided = 0; 
int   show_help = 1;                //help toggle
int   stereo = 0;                   //stereo init variable, no use... not supported by my GPU
int   benchmark = 0;
int   centerZ = -3;
float auto_rotate = 0;
int xrotate = 0;
int yrotate = 1;
int zrotate = 0;

int lighting = 1;


//int   smooth_hint = 0;
int Help_Font=(int)GLUT_BITMAP_8_BY_13;  
int linestart = 10;                 //start point on y axis for text lines
int linespace = 20;                 //space between text lines



//Lighting Variables///////////////////////////////////////////////////////////
GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };

GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 };
GLfloat mat_diffuse[] = { 0.8, 0.8, 0.8, 1.0 };
GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 };
GLfloat high_shininess[] = { 100.0 };



//Stereo Variables/////////////////////////////////////////////////////////////
GLfloat EyeSep =  0.05; //Eye Setup TODO
GLfloat EyeBack = 0.0;
GLfloat FocusX = 0.0;
GLfloat FocusY = 0.0;
GLfloat FocusZ = -1.0;



//Standard Glut interface functions////////////////////////////////////////////                                    
void Reshape(int w,int h);
void Mouse(int button, int state, int x, int y);
void Motion(int x, int y);
void Keyboard(unsigned char key, int x, int y);
void Display(void);
void AutoSpin(void);


//Model and help display functoins/////////////////////////////////////////////
void DrawModel(void);
void DrawAxis( float scale );
void HelpDisplay(GLint ww, GLint wh);
void HelpRenderBitmapString(float x, float y, void *font,char *string);



/*****************************************************************************/
/*Main Functions**************************************************************/
/*****************************************************************************/


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char** argv)
{
	// parse params
    if (argc > 2)  //make sure at least 3 args, program and file
    {
		strcpy (texfilename, argv[argc-1]);	//get the last arg as the texture file name;

        strcpy (filename, argv[argc-2]);	//get the last but one arg as the file always

        for (int i = 0; i<argc; i++)     //check for startup params
        {
           // if(strstr(argv[i], "-s")) stereo = 1; 
          //  if(strstr(argv[i], "-f")) full_screen = 1; 
        }
    }
    else           //user only entered program name, help them
    {
		printf("\nUsage: objviewer <obj filename> <texture filename | null(NULL)>");
        exit(0);
    }

	glutInit(&argc, argv);
	
	if(stereo) glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STEREO);
	else glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);


	glutInitWindowSize(1024, 768);                // Window Size If We Start In Windowed Mode
	glutCreateWindow("Wavefront Obj File Viewer");

	if(full_screen) glutFullScreen();


	glutDisplayFunc(Display);
	glutKeyboardFunc(Keyboard);
	getMatrix();
	glClearColor(0.0, 0.0, 0.0, 0.0);
	glClearAccum(0.0, 0.0, 0.0, 0.0);
	glutReshapeFunc(Reshape);
	glutMouseFunc(Mouse);
	glutMotionFunc(Motion);
	glutIdleFunc(NULL);

	// Here align to 1 byte, default is 4 bytes
	// for efficiency, it's better to use 4 or 8 bytes for alignment
	// glReadPixels/glWritePixels depend on these settings
	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	glLightfv(GL_LIGHT0, GL_POSITION, light_position);

	glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
	glMaterialfv(GL_FRONT, GL_SHININESS, high_shininess);

	if (lighting) glEnable(GL_LIGHTING);
	if (lighting) glEnable(GL_LIGHT0);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glEnable (GL_TEXTURE_2D);

	if (!pmodel) //load up the model and texture
	{
		Transform identity;
		Reference<Material> mat = new NullMaterial();
		Reference<Texture<float>> atex= new ConstantTexture<float>(0.f);
		pmodel = new TriangleMesh(&identity, &identity, false, mat, filename, true, atex);
		if (!pmodel)
		{
			Error("Loading obj %s error\n", filename);
			exit(0);
		}
		pmodel->Refine(shapes);
		Debug("%d triangles was loaded from %s\n", shapes.size(), filename);

		// load texture.

		if(strcmp(texfilename, "null") && strcmp(texfilename, "NULL")) {
			gltexture = new GLTexture(texfilename);
		}
	}

    glutMainLoop();

	// do some cleanup
	SAFE_DELETE(pmodel);
	SAFE_DELETE(gltexture);

    return 0;
}




/*****************************************************************************/
/*Mouse Functions*************************************************************/
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void Reshape(int w,int h)
{
	glViewport(0,0,w,h);

    ww = w;
    wh = h;

	top    =  1.0;
	bottom = -1.0;
	left   = -(double)w/(double)h;
	right  = -left;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	//glOrtho(left,right,bottom,top,zNear,zFar);  //Ortho
    gluPerspective(fovy,(double)w/(double)h, zNear, zFar);    //PErspective for stereo

	glMatrixMode(GL_MODELVIEW);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void Mouse(int button, int state, int x, int y)
{
	int viewport[4];

	mouseX = x;
	mouseY = y;

	if (state==GLUT_UP)
		switch (button)
		{
			case GLUT_LEFT_BUTTON:   mouseLeft   = false; break;
			case GLUT_MIDDLE_BUTTON: mouseMiddle = false; break;
			case GLUT_RIGHT_BUTTON:  mouseRight  = false; break;
		}
	else
		switch (button)
		{
			case GLUT_LEFT_BUTTON:	 mouseLeft   = true; break;
			case GLUT_MIDDLE_BUTTON: mouseMiddle = true; break;
			case GLUT_RIGHT_BUTTON:	 mouseRight  = true; break;
		}

	glGetIntegerv(GL_VIEWPORT,viewport);
	pos(&dragPosX,&dragPosY,&dragPosZ,x,y,viewport);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void Motion(int x, int y)
{
	bool changed = false;

	const int dx = x - mouseX;
	const int dy = y - mouseY;

	int viewport[4];
	glGetIntegerv(GL_VIEWPORT,viewport);

	if (dx==0 && dy==0)
		return;

	if (mouseMiddle || (mouseLeft && mouseRight))
	{
		//double s = exp((double)dy*0.01);
		//glScalef(s,s,s);
		//if(abs(prev_z) <= 1.0)

		glLoadIdentity();
		glTranslatef(0,0,dy*0.01);
		glMultMatrixd(matrix);



		changed = true;
	}
	else
	if (mouseLeft)
	{
		double ax,ay,az;
		double bx,by,bz;
		double angle;

		ax = dy;
		ay = dx;
		az = 0.0;
		angle = vlen(ax,ay,az)/(double)(viewport[2]+1)*180.0;

		/* Use inverse matrix to determine local axis of rotation */

		bx = matrixI[0]*ax + matrixI[4]*ay + matrixI[8]*az;
		by = matrixI[1]*ax + matrixI[5]*ay + matrixI[9]*az;
		bz = matrixI[2]*ax + matrixI[6]*ay + matrixI[10]*az;

		glRotatef(angle,bx,by,bz);
	
		changed = true;
	}
	else
	if (mouseRight)
	{
		double px,py,pz;

		pos(&px,&py,&pz,x,y,viewport);

		glLoadIdentity();
		glTranslatef(px-dragPosX,py-dragPosY,pz-dragPosZ);
		glMultMatrixd(matrix);

		dragPosX = px;
		dragPosY = py;
		dragPosZ = pz;

		changed = true;
	}

	mouseX = x;
	mouseY = y;

	if (changed)
	{	
		getMatrix();
		glutPostRedisplay();
	}
}


void AutoSpin(void)
{
    if(xrotate || yrotate || zrotate)
    {
		glRotatef(1,xrotate,yrotate,zrotate);
		getMatrix();	
		glutPostRedisplay();
    }

}


/*****************************************************************************/
/*Keyboard input**************************************************************/
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void Keyboard(unsigned char key, int x, int y)
{

	switch (key)
	{
	case 'h':
	case 'H': {	
                show_help = !show_help;        
                break;
			  }	
	case 'r':
	case 'R': {	
                //Resetting Scene
                for (int i = 0; i < 16; i++)
                {
                  if (i == 0 || i == 5 || i == 10 || i == 15)
                  {   
                      matrix[i] = 1;
                  }
                  else
                  {   
                      matrix[i] = 0;
                  }
                }
				prev_z = 0;
		        glLoadIdentity();
		        glMultMatrixd(matrix);
                getMatrix();
                break;
			  }	
	case 'a':
	case 'A': {	
                show_axis = !show_axis;
                break;
			  }	
	case 'b':
	case 'B':
		{
			benchmark = !benchmark;
			if (benchmark)
			    glutIdleFunc(AutoSpin);
			else
			    glutIdleFunc(NULL);
			   break;
		}
    case 'x':
    case 'X':
        {
            xrotate = !xrotate;
            break;
        }
    case 'y':
    case 'Y':
        {
            yrotate = !yrotate;
            break;
        }
    case 'z':
    case 'Z':
        {
            zrotate = !zrotate;
            break;
        }
            
	case 'l':
	case 'L':
			lighting = !lighting;
			break;
	case 'w':
	case 'W': {	
                wireframe = !wireframe;     
                break;
			  }	
	case 'd':
	case 'D': {	
                two_sided = !two_sided;     
                break;
			  }	
	case 's':
	case 'S': {	
                SaveResult(ww, wh, "ObjViewer.bmp");     
                break;
			  }	
	case 'm':
	case 'M': {	
                material = !material;     
                if(!material && textured) textured = 0;
                break;
			  }	
	case 't':
	case 'T': {	
                textured = !textured;     
                if(!material && textured) material = 1;
                break;
			  }	
	case 'f':
	case 'F': {	
               // glmFlipModelTextures(pmodel);
                break;
			  }	
	case ',':
	case '<': {	
                EyeSep -= 0.025;
                break;
			  }	
	case '.':
	case '>': {	
                EyeSep += 0.025;
                break;
			  }	
	case '+':
	case '=':
              {	
                FocusZ -= 0.25;
                break;
			  }	
	case '-': 
	case '_': 
              {	
                FocusZ += 0.25;
                break;
			  }	
	case 9: 
              {	
                if(!full_screen)
                {
                    glutFullScreen();
                    full_screen = 1;
                }

                break;
			  }	
    case 27:
      exit(0);
      break;
    default : {
				fflush(stdin);
				break;
			  }	//flush all other input 				
	}
	fflush(stdin);						//flush input
	glutPostRedisplay();									//redisplay afterword
}



/*****************************************************************************/
/*Matrix and Vector Utility functions ****************************************/
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
double vlen(double x,double y,double z)
{
	return sqrt(x*x+y*y+z*z);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void pos(double *px,double *py,double *pz,const int x,const int y,const int *viewport)
{
	/*
	  Use the ortho projection and viewport information
	  to map from mouse co-ordinates back into world 
	  co-ordinates
	*/

	*px = (double)(x-viewport[0])/(double)(viewport[2]);
	*py = (double)(y-viewport[1])/(double)(viewport[3]);

	*px = left + (*px)*(right-left);
	*py = top  + (*py)*(bottom-top);
	*pz = zNear;
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void getMatrix()
{
	glGetDoublev(GL_MODELVIEW_MATRIX,matrix);
	invertMatrix(matrix,matrixI);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
//
// From Mesa-2.2\src\glu\project.c
//
//
// Compute the inverse of a 4x4 matrix.  Contributed by scotter@lafn.org
//
void 
invertMatrix(const GLdouble *m, GLdouble *out )
{

/* NB. OpenGL Matrices are COLUMN major. */
#define MAT(m,r,c) (m)[(c)*4+(r)]

/* Here's some shorthand converting standard (row,column) to index. */
#define m11 MAT(m,0,0)
#define m12 MAT(m,0,1)
#define m13 MAT(m,0,2)
#define m14 MAT(m,0,3)
#define m21 MAT(m,1,0)
#define m22 MAT(m,1,1)
#define m23 MAT(m,1,2)
#define m24 MAT(m,1,3)
#define m31 MAT(m,2,0)
#define m32 MAT(m,2,1)
#define m33 MAT(m,2,2)
#define m34 MAT(m,2,3)
#define m41 MAT(m,3,0)
#define m42 MAT(m,3,1)
#define m43 MAT(m,3,2)
#define m44 MAT(m,3,3)

   GLdouble det;
   GLdouble d12, d13, d23, d24, d34, d41;
   GLdouble tmp[16]; /* Allow out == in. */

   /* Inverse = adjoint / det. (See linear algebra texts.)*/

   /* pre-compute 2x2 dets for last two rows when computing */
   /* cofactors of first two rows. */
   d12 = (m31*m42-m41*m32);
   d13 = (m31*m43-m41*m33);
   d23 = (m32*m43-m42*m33);
   d24 = (m32*m44-m42*m34);
   d34 = (m33*m44-m43*m34);
   d41 = (m34*m41-m44*m31);

   tmp[0] =  (m22 * d34 - m23 * d24 + m24 * d23);
   tmp[1] = -(m21 * d34 + m23 * d41 + m24 * d13);
   tmp[2] =  (m21 * d24 + m22 * d41 + m24 * d12);
   tmp[3] = -(m21 * d23 - m22 * d13 + m23 * d12);

   /* Compute determinant as early as possible using these cofactors. */
   det = m11 * tmp[0] + m12 * tmp[1] + m13 * tmp[2] + m14 * tmp[3];

   /* Run singularity test. */
   if (det == 0.0) {
      /* printf("invertmatrix: Warning: Singular matrix.\n"); */
//	  memcpy(out,_identity,16*sizeof(double));
   }
   else {
      GLdouble invDet = 1.0 / det;
      /* Compute rest of inverse. */
      tmp[0] *= invDet;
      tmp[1] *= invDet;
      tmp[2] *= invDet;
      tmp[3] *= invDet;

      tmp[4] = -(m12 * d34 - m13 * d24 + m14 * d23) * invDet;
      tmp[5] =  (m11 * d34 + m13 * d41 + m14 * d13) * invDet;
      tmp[6] = -(m11 * d24 + m12 * d41 + m14 * d12) * invDet;
      tmp[7] =  (m11 * d23 - m12 * d13 + m13 * d12) * invDet;

      /* Pre-compute 2x2 dets for first two rows when computing */
      /* cofactors of last two rows. */
      d12 = m11*m22-m21*m12;
      d13 = m11*m23-m21*m13;
      d23 = m12*m23-m22*m13;
      d24 = m12*m24-m22*m14;
      d34 = m13*m24-m23*m14;
      d41 = m14*m21-m24*m11;

      tmp[8] =  (m42 * d34 - m43 * d24 + m44 * d23) * invDet;
      tmp[9] = -(m41 * d34 + m43 * d41 + m44 * d13) * invDet;
      tmp[10] =  (m41 * d24 + m42 * d41 + m44 * d12) * invDet;
      tmp[11] = -(m41 * d23 - m42 * d13 + m43 * d12) * invDet;
      tmp[12] = -(m32 * d34 - m33 * d24 + m34 * d23) * invDet;
      tmp[13] =  (m31 * d34 + m33 * d41 + m34 * d13) * invDet;
      tmp[14] = -(m31 * d24 + m32 * d41 + m34 * d12) * invDet;
      tmp[15] =  (m31 * d23 - m32 * d13 + m33 * d12) * invDet;

      memcpy(out, tmp, 16*sizeof(GLdouble));
   }

#undef m11
#undef m12
#undef m13
#undef m14
#undef m21
#undef m22
#undef m23
#undef m24
#undef m31
#undef m32
#undef m33
#undef m34
#undef m41
#undef m42
#undef m43
#undef m44
#undef MAT
}




/*****************************************************************************/
/*OpenGL Display Functions****************************************************/
/*****************************************************************************/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void DrawModel( void )
{
    glPushMatrix();

	if(gltexture) {
		if(textured)
			gltexture->Enable();
		else
			gltexture->Disable();
	}
	if(!textured)
		glDisable(GL_TEXTURE_2D);

    for(uint i = 0; i < shapes.size(); i++)
		shapes[i]->RenderGL(textured);

    glPopMatrix();
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void DrawAxis( float scale )
{
  glPushMatrix();
  glDisable(GL_LIGHTING);
  glDisable(GL_TEXTURE_2D);
  glScalef( scale, scale, scale );

  glBegin( GL_LINES );
 
  glColor3f( 1.0, 0.0, 0.0 );
  glVertex3f( .8f, 0.05f, 0.0 );  glVertex3f( 1.0, 0.25f, 0.0 ); // Letter X
  glVertex3f( 0.8f, .25f, 0.0 );  glVertex3f( 1.0, 0.05f, 0.0 );
  glVertex3f( 0.0, 0.0, 0.0 );  glVertex3f( 1.0, 0.0, 0.0 );     // X axis

  glColor3f( 0.0, 1.0, 0.0 );
  glVertex3f( 0.0, 0.0, 0.0 );  glVertex3f( 0.0, 1.0, 0.0 );	 // Y axis

  glColor3f( 0.0, 0.0, 1.0 );
  glVertex3f( 0.0, 0.0, 0.0 );  glVertex3f( 0.0, 0.0, 1.0 );	 // Z axis
  glEnd();
  if (lighting) glEnable(GL_LIGHTING);
  if (lighting) glEnable(GL_TEXTURE_2D);
  glColor3f( 1.0, 1.0, 1.0 );
  glPopMatrix();
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void HelpDisplay(GLint ww, GLint wh)
{
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
	glColor3f(1.0f,1.0f,1.0f);


    // switch to projection mode
	glMatrixMode(GL_PROJECTION);
	// save previous matrix which contains the 
	//settings for the perspective projection
	glPushMatrix();
	// reset matrix
	glLoadIdentity();
	// set a 2D orthographic projection
	gluOrtho2D(0, ww, 0, wh);
	// invert the y axis, down is positive
	glScalef(1, -1, 1);
	// mover the origin from the bottom left corner
	// to the upper left corner
	glTranslatef(0, -wh, 0);
	glMatrixMode(GL_MODELVIEW);


	glPushMatrix();
	glLoadIdentity();
    linestart = 10;

	HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "Help Menu");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "---------");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "H/h = Toggle Help Menu");
    if (!full_screen) HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "TAB = Activate Full Screen");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "Esc = Exits Program");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "R/r = Reset Position");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "A/a = Toggle Axis");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "W/w = Toggle Wireframe");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "D/d = Toggle Double Sided Polygons");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "S/s = Toggle Smooth Shading");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "M/m = Toggle Materials");
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "T/t = Toggle Textures");   
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "B/b = Toggle Auto Rotate");    
    HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "F/f = Flip Textures");
	
    if(stereo)
    {
        HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "Stereo Variables");
        HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "----------------");
        HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "</, = Decrease Eye Separation");
        HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, ">/. = Increase Eye Separation");
        HelpRenderBitmapString(30,linestart+=linespace,(void *)Help_Font, "+/- = Increase/Decrease Focus Distance");
    }

	glPopMatrix();

	// set the current matrix to GL_PROJECTION
	glMatrixMode(GL_PROJECTION);
	// restore previous settings
	glPopMatrix();
	// get back to GL_MODELVIEW matrix
	glMatrixMode(GL_MODELVIEW);

    if (lighting) glEnable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void HelpRenderBitmapString(float x, float y, void *font,char *string)
{
  char *c;
  // set position to start drawing fonts
  glRasterPos2f(x, y);
  // loop all the characters in the string
  for (c=string; *c != '\0'; c++) {
    glutBitmapCharacter(font, *c);
  }
}


///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void Display(void)
{ 
	if (lighting)
	{
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
	}
	else
	{
		glDisable (GL_LIGHTING);
		glDisable (GL_LIGHT0);
	}

    if (stereo)
    {
        //Clear right and left eye buffers
        glDrawBuffer(GL_BACK_RIGHT);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        //Right Eye
        //glMatrixMode(GL_PROJECTION);
        //glLoadIdentity();
        //gluPerspective(45,(double)ww/(double)wh, 0.1, zFar);

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glDrawBuffer(GL_BACK_RIGHT);
        glLoadIdentity();
        gluLookAt(EyeSep/2, 0, EyeBack,
	              FocusX, FocusY, FocusZ,
	              0,1,0);

        glTranslatef(0,0,centerZ);
        glMultMatrixd(matrix);
    
        if ( show_axis )
            DrawAxis(1.0f);
        if (wireframe)							//if Wireframe is checked
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //draw wireframe
        else										//else
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //draw filled polygons

        DrawModel();
        glTranslatef(0,0,-centerZ);
        glPopMatrix();

        
        //Left Eye
        //glMatrixMode(GL_PROJECTION);
        //glLoadIdentity();
        //gluPerspective(45,(double)ww/(double)wh, 0.1, zFar);
	    glDrawBuffer(GL_BACK_LEFT);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    

        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        //glDrawBuffer(GL_BACK_LEFT);
        glLoadIdentity();
        gluLookAt(-EyeSep/2, 0, EyeBack,
	              FocusX, FocusY, FocusZ,
	              0,1,0);
        glPushMatrix ();
    
        glTranslatef(0,0,centerZ);
        glMultMatrixd(matrix);
    
        if ( show_axis )
            DrawAxis(1.0f);
        if (wireframe)							//if Wireframe is checked
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //draw wireframe
        else										//else
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //draw filled polygons

        DrawModel();
        glTranslatef(0,0,-centerZ);
        glPopMatrix ();
        glPopMatrix();
    }
    else        //NON stereo mode
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glPushMatrix ();
    
        glLoadIdentity();
        glTranslatef(0,0,centerZ);   //to center object down Z
        glMultMatrixd(matrix);
	    
        if ( show_axis )
            DrawAxis(1.0f);
        if (wireframe)							//if Wireframe is checked
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); //draw wireframe
        else										//else
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //draw filled polygons

        DrawModel();
        glTranslatef(0,0,-centerZ);   //to center object down Z
        glPopMatrix ();
    }

    if (show_help)  HelpDisplay(ww, wh);


    glutSwapBuffers();
}



